#ifndef _BUILTINFUNCTION_HPP
#define _BUILTINFUNCTION_HPP

#include "BuiltinObject.hpp"
#include <map>
#include <string>
#include "FunctionPrint.hpp"
#include "FunctionEcho.hpp"
#include "FunctionPrintf.hpp"
#include "FunctionFgets.hpp"
#include "FunctionAbs.hpp"
#include "FunctionFloor.hpp"
#include "FunctionCeil.hpp"
#include "FunctionRound.hpp"
#include "FunctionArrayCount.hpp"
#include "FunctionArrayKeys.hpp"
#include "FunctionArrayValues.hpp"
#include "FunctionArrayKeyExist.hpp"
#include "FunctionArrayPush.hpp"
#include "FunctionStrlen.hpp"
#include "FunctionStrToUpper.hpp"
#include "FunctionSubStr.hpp"
#include "FunctionStrRev.hpp"
#include "FunctionStrPos.hpp"
#include "FunctionStrStr.hpp"
#include "FunctionExplode.hpp"
#include "FunctionImplode.hpp"
#include "FunctionChr.hpp"
#include "FunctionOrd.hpp"
#include "FunctionObjectKeys.hpp"
#include "FunctionObjectValues.hpp"
#include "BuiltinClass.hpp"
#include "TcpServer.hpp"
#include "MysqlServer.hpp"
#include "FunctionJsonEncode.hpp"
#include "FunctionJsonDecode.hpp"
#include "FunctionFileGetContents.hpp"

using namespace object;
using namespace std;

namespace builtin {
	class BuiltinFunction {
	public:
		BuiltinFunction(std::function<void(FunctionObject*, std::vector<Object*>)> call) 
			: _call(call) {
			initBuiltinFunction();
			initBuiltinClass();
		}

		void initBuiltinFunction() {
			BuiltinObject* print = new BuiltinObject;
			print->_func = FunctionPrint();
			_funcs["print"] = print;

			BuiltinObject* echo = new BuiltinObject;
			echo->_func = FunctionEcho();
			_funcs["echo"] = echo;

			BuiltinObject* printf = new BuiltinObject;
			printf->_func = FunctionPrintf();
			_funcs["printf"] = printf;

			BuiltinObject* fgets = new BuiltinObject;
			fgets->_func = FunctionFgets();
			_funcs["fgets"] = fgets;

			BuiltinObject* abs = new BuiltinObject;
			abs->_func = FunctionAbs();
			_funcs["abs"] = abs;

			BuiltinObject* floor = new BuiltinObject;
			floor->_func = FunctionFloor();
			_funcs["floor"] = floor;

			BuiltinObject* ceil = new BuiltinObject;
			ceil->_func = FunctionCeil();
			_funcs["ceil"] = ceil;

			BuiltinObject* round = new BuiltinObject;
			round->_func = FunctionRound();
			_funcs["round"] = round;

			BuiltinObject* array_count = new BuiltinObject;
			array_count->_func = FunctionArrayCount();
			_funcs["array_count"] = array_count;

			BuiltinObject* array_keys = new BuiltinObject;
			array_keys->_func = FunctionArrayKeys();
			_funcs["array_keys"] = array_keys;

			BuiltinObject* array_values = new BuiltinObject;
			array_values->_func = FunctionArrayValues();
			_funcs["array_values"] = array_values;

			BuiltinObject* array_key_exists = new BuiltinObject;
			array_key_exists->_func = FunctionArrayKeyExist();
			_funcs["array_key_exists"] = array_key_exists;

			BuiltinObject* array_push = new BuiltinObject;
			array_push->_func = FunctionArrayPush();
			_funcs["array_push"] = array_push;

			BuiltinObject* strlen = new BuiltinObject;
			strlen->_func = FunctionStrlen();
			_funcs["strlen"] = strlen;

			BuiltinObject* strtoupper = new BuiltinObject;
			strtoupper->_func = FunctionStrToUpper();
			_funcs["strtoupper"] = strtoupper;

			BuiltinObject* substr = new BuiltinObject;
			substr->_func = FunctionSubStr();
			_funcs["substr"] = substr;

			BuiltinObject* strrev = new BuiltinObject;
			strrev->_func = FunctionStrRev();
			_funcs["strrev"] = strrev;

			BuiltinObject* strpos = new BuiltinObject;
			strpos->_func = FunctionStrPos();
			_funcs["strpos"] = strpos;

			BuiltinObject* strstr = new BuiltinObject;
			strstr->_func = FunctionStrStr();
			_funcs["strstr"] = strstr;

			BuiltinObject* explode = new BuiltinObject;
			explode->_func = FunctionExplode();
			_funcs["explode"] = explode;

			BuiltinObject* implode = new BuiltinObject;
			implode->_func = FunctionImplode();
			_funcs["implode"] = implode;

			BuiltinObject* chr = new BuiltinObject;
			chr->_func = FunctionChr();
			_funcs["chr"] = chr;

			BuiltinObject* ord = new BuiltinObject;
			ord->_func = FunctionOrd();
			_funcs["ord"] = ord;

			BuiltinObject* object_keys = new BuiltinObject;
			object_keys->_func = FunctionObjectKeys();
			_funcs["object_keys"] = object_keys;
		
			BuiltinObject* object_values = new BuiltinObject;
			object_values->_func = FunctionObjectValues();
			_funcs["object_values"] = object_values;

			BuiltinObject* json_encode = new BuiltinObject;
			json_encode->_func = FunctionJsonEncode();
			_funcs["json_encode"] = json_encode;

			BuiltinObject* json_decode = new BuiltinObject;
			json_decode->_func = FunctionJsonDecode();
			_funcs["json_decode"] = json_decode;

			BuiltinObject* file_get_contents = new BuiltinObject;
			file_get_contents->_func = FunctionFileGetContents();
			_funcs["file_get_contents"] = file_get_contents;
		}

		void initBuiltinClass() {
			TcpServer* tcpServer = new TcpServer(_call);
			BuiltinClass* s = new BuiltinClass;
			s->_funcs.insert(std::make_pair("on", 
				std::bind(&TcpServer::on, tcpServer, std::placeholders::_1)));
			s->_funcs.insert(std::make_pair("start",
				std::bind(&TcpServer::start, tcpServer, std::placeholders::_1)));
			s->_funcs.insert(std::make_pair("stop",
				std::bind(&TcpServer::stop, tcpServer, std::placeholders::_1)));
			s->_funcs.insert(std::make_pair("send",
				std::bind(&TcpServer::send, tcpServer, std::placeholders::_1)));
			_classes.insert(std::make_pair("TcpServer", s));

			MysqlServer* mysqlServer = new MysqlServer;
			BuiltinClass* mysql = new BuiltinClass;
			mysql->_funcs.insert(std::make_pair("connect",
				std::bind(&MysqlServer::connect, mysqlServer, std::placeholders::_1)));
			mysql->_funcs.insert(std::make_pair("query",
				std::bind(&MysqlServer::query, mysqlServer, std::placeholders::_1)));
			mysql->_funcs.insert(std::make_pair("execute",
				std::bind(&MysqlServer::execute, mysqlServer, std::placeholders::_1)));
			mysql->_funcs.insert(std::make_pair("fetch_data",
				std::bind(&MysqlServer::fetch_data, mysqlServer, std::placeholders::_1)));
			_classes.insert(std::make_pair("TcpServer", s));
			mysql->_funcs.insert(std::make_pair("close",
				std::bind(&MysqlServer::close, mysqlServer, std::placeholders::_1)));
			_classes.insert(std::make_pair("MysqlServer", mysql));
		}


		BuiltinObject* GetInnerFunc(const std::string& funcName) {
			auto itr = _funcs.find(funcName);
			if (itr != _funcs.end()) {
				return _funcs[funcName];
			}
			return nullptr;
		}


		BuiltinClass* GetInnerClass(const std::string& className) {
			auto itr = _classes.find(className);
			if (itr != _classes.end()) {
				return _classes[className];
			}
			return nullptr;
		}

	private:
		std::map<std::string, BuiltinObject*> _funcs;
		std::map<std::string, BuiltinClass*> _classes;
		std::function<void(FunctionObject*, std::vector<Object*>)> _call;
	};
}

#endif // !_BUILTINFUNCTION_HPP
